package home.project.misc;

import openfl.display.Graphics;
import openfl.display.BitmapData;
import openfl.geom.Rectangle;
import openfl.geom.Point;
import openfl.geom.Matrix;
import openfl.Assets;

/**
 * ...
 * @author an.vuongngocduy
 */

class Spritemodule { 
	public var image:Int;		
	public var name:String;	
	public var region:Rectangle; 
	public function new(){}
}

class SpritemoduleLinker {
	public var id:Int;
	public var module:Int;
	public var placement:Point;
	public var transform:Matrix;
	public function new(){}
}

class Spriteframe {
	public var name:String;
	public var rect:Rectangle;
	public var modules:Array<SpritemoduleLinker>;
	public function new(){}
}
 
class SpriteframeLinker {
	public var id:Int;
	public var frame:Int;
	public var time:Int;
	public var bbox:Rect;	
	public var placement:Point;
	public function new(){}
}

class Spriteanim {
	public var name:String;
	public var fps:Float;
	public var frames:Array<SpriteframeLinker>;
	public function new(){}
}

class Spritesheet extends Entity {
	
	private var __images:Array<String>;
	private var __modules:Array<Spritemodule>;
	private var __frames:Array<Spriteframe>;
	private var __anims:Array<Spriteanim>;

	private var __isLoop:Bool;
	private var __curAnim:Int;
	private var __curAnimFrame:Int;
	private var __curFps:Int;
	private var __lastTime:Float;
	
	public function new() {
		super();
	}
	
	public static function create(p_path:String, p_numCol:Float, p_numRow:Float, p_width:Float, p_height:Float, p_xOffset:Float, p_yOffset:Float):Spritesheet {
		var spr:Spritesheet = new Spritesheet();
		spr.__curFps = 10;
		spr.__curAnim = 0;
		spr.__curAnimFrame = 0;
		spr.__lastTime = 0.0;
		spr.__isLoop = true;	
		spr.__images = new Array<String>();
		spr.__modules = new Array<Spritemodule>();
		spr.__frames = new Array<Spriteframe>();
		spr.__anims = new Array<Spriteanim>();
		
		spr.__images.push(p_path);	
		var bm:BitmapData = Assets.getBitmapData(p_path);
		
		var x:Float = 0;
		var y:Float = 0;
		var r:Float = 0;
		var c:Float = 0;
		while (y < bm.height && r < p_numRow) {			
			while(x < bm.width && c < p_numCol) {
				var sm:Spritemodule = new Spritemodule();
				sm.image = spr.__images.length - 1;
				sm.region = new Rectangle();
				sm.region.x = x;
				sm.region.y = y;
				sm.region.width = p_width;
				sm.region.height = p_height;				
				spr.__modules.push(sm);
				
				var sf:Spriteframe = new Spriteframe();
				sf.rect = new Rectangle();
				sf.modules = new Array<SpritemoduleLinker>();
				
				var mdl:SpritemoduleLinker = new SpritemoduleLinker();
				mdl.module = spr.__modules.length - 1;
				mdl.placement = new Point(0, 0);
				
				sf.modules.push(mdl);
				
				spr.__frames.push(sf); //finish loading a frame
				
				x += p_width + p_xOffset;
				c++;
			}
			y += p_height + p_yOffset;
			r++;
		}
		
		return spr;
	}
	
	public static function load(p_path:String):Spritesheet {
		var spr:Spritesheet = new Spritesheet();
		spr.__curFps = 10;
		spr.__curAnim = 0;
		spr.__curAnimFrame = 0;
		spr.__lastTime = 0.0;
		spr.__isLoop = true;	
		spr.__images = new Array<String>();
		spr.__modules = new Array<Spritemodule>();
		spr.__frames = new Array<Spriteframe>();
		spr.__anims = new Array<Spriteanim>();
		
		var root:Xml = Xml.parse(Assets.getText(p_path));
		
		var sprNode:Xml = root.firstElement();
		
		spr.name = sprNode.get("name");
		
		for (node in sprNode.elements()) {
			if (node.nodeName == "Images") {
				for (img in node.elements()) {
					spr.__images.push(img.get("path"));
				}
			}
			else if (node.nodeName == "Modules") {
				for (mod in node.elements()) {
					var sm:Spritemodule = new Spritemodule();
					
					sm.image = Std.parseInt(mod.get("ImageId"));
					sm.name = mod.get("comment");
					sm.region = new Rectangle();
					sm.region.x = Std.parseInt(mod.get("x"));
					sm.region.y = Std.parseInt(mod.get("y"));
					sm.region.width = Std.parseInt(mod.get("width"));
					sm.region.height = Std.parseInt(mod.get("height"));				
					spr.__modules.push(sm);
				}
			}
			else if (node.nodeName == "Frames") {
				for (fr in node.elements()) {
					var sf:Spriteframe = new Spriteframe();
					sf.rect = new Rectangle();
					sf.name = fr.get("name");
					sf.modules = new Array<SpritemoduleLinker>();
					
					for (node2 in fr.elements()) {
						var mdl:SpritemoduleLinker = new SpritemoduleLinker();
						mdl.module = Std.parseInt(node2.get("moduleId"));
						mdl.placement = new Point(Std.parseInt(node2.get("x")), Std.parseInt(node2.get("y")));
						
						sf.rect.left = (sf.rect.left == 0 ||  mdl.placement.x < sf.rect.left) 
									? mdl.placement.x : sf.rect.left;
						
						sf.rect.right = (sf.rect.right == 0 || (mdl.placement.x  + spr.__modules[mdl.module].region.width) > sf.rect.right) 
									? (mdl.placement.x  + spr.__modules[mdl.module].region.width) : sf.rect.right;
									
						sf.rect.top = (sf.rect.top == 0 ||  mdl.placement.y < sf.rect.top) 
									? mdl.placement.y : sf.rect.top;
						
						sf.rect.bottom = (sf.rect.bottom == 0 || (mdl.placement.y  + spr.__modules[mdl.module].region.height) > sf.rect.bottom) 
									? (mdl.placement.y  + spr.__modules[mdl.module].region.height) : sf.rect.bottom;
									
						sf.modules.push(mdl);
					}
					
					spr.__frames.push(sf);
				}
			}
			else if (node.nodeName == "Animations") {
				for (an in node.elements()) {
					var sa:Spriteanim = new Spriteanim();
					
					sa.name = an.get("name");
					sa.frames = new Array<SpriteframeLinker>();
		
					for (node2 in an.elements()) {
						var frl:SpriteframeLinker = new SpriteframeLinker();
						frl.frame = Std.parseInt(node2.get("frameId"));
						frl.bbox = new Rect(spr, Std.parseInt(node2.get("x")), Std.parseInt(node2.get("y")),
												Std.parseInt(node2.get("width")), Std.parseInt(node2.get("height")));
						frl.placement = new Point(Std.parseInt(node2.get("x")), Std.parseInt(node2.get("y")));
						sa.frames.push(frl);
					}
					spr.__anims.push(sa);
				}
			}
		}
		return spr;
	}
	
	public override function get_boundingBoxes():Iterator<Rect> {
		__boundingBoxes.splice(0, __boundingBoxes.length);
		__boundingBoxes.push(__anims[__curAnim].frames[__curAnimFrame].bbox);
		return __boundingBoxes.iterator();
	}
	
	public override function draw(p_g:Graphics):Void {
		if (Camera.current != null ? !Camera.current.contains(this) : true) return;
		var cam_x:Float = Camera.current != null ? Camera.current.absoluteTransform.tx : 0;
		var cam_y:Float = Camera.current != null ? Camera.current.absoluteTransform.ty : 0;
		drawFrame(p_g,  __anims[__curAnim].frames[__curAnimFrame].frame, absoluteTransform.tx - cam_x, absoluteTransform.ty - cam_y);
	}
	
	public function addAnimation(p_name:String, p_frameIndices:Array<Int>, ?p_fps:Int):Void {
		__curFps = p_fps == null ? __curFps : p_fps;
		var anim:Spriteanim = new Spriteanim();
		anim.name = p_name;
		anim.frames = new Array<SpriteframeLinker>();
		
		for (idx in p_frameIndices) {
			if (idx >= __frames.length) continue;
			var flnk:SpriteframeLinker = new SpriteframeLinker();
			flnk.frame = idx;
			flnk.placement = new Point(0, 0);
			flnk.bbox = new Rect(this, 0, 0, __frames[idx].rect.width, __frames[idx].rect.height);
			anim.frames.push(flnk);
		}
		__anims.push(anim);
	}
	
	public function drawModule(p_g:Graphics, p_moduleIdx:Int, p_x:Float, p_y:Float, p_transform:Matrix = null):Void {
		var pixels:BitmapData = Assets.getBitmapData("img/" + __images[__modules[p_moduleIdx].image]);
		
		var mat:Matrix = new Matrix();
		mat.translate(p_x - __modules[p_moduleIdx].region.x, p_y - __modules[p_moduleIdx].region.y);
		if(p_transform != null)
			p_g.beginBitmapFill(pixels, p_transform);
		else
			p_g.beginBitmapFill(pixels, mat, false);
		p_g.drawRect(p_x, p_y, __modules[p_moduleIdx].region.width, __modules[p_moduleIdx].region.height);
		p_g.endFill();
	}
	
	public function drawFrame(p_g:Graphics, p_frameIdx:Int, p_x:Float, p_y:Float):Void {
		var count:Int = 0;	
		while (count < __frames[p_frameIdx].modules.length) {		
			var moLink:SpritemoduleLinker = __frames[p_frameIdx].modules[count];
			var local_x:Float = p_x + moLink.placement.x;
			var local_y:Float = p_y + moLink.placement.y;
			drawModule(p_g, moLink.module, local_x, local_y, moLink.transform);
			count++;
		}
	}
}